/**
 * @addtogroup BAS
 * @{
 * @file
 * 容器公用操作
 */
#include <string.h>
#include "bas/bas_container.h"

static const char mcaId[] = "$Id$";

static void _basContainerBuildInStringDestory(char **pcaString);
static int _basContainerBuildInStringCopy(char **pcaStringTo, const char **pcaStringFrom);
static bool _basContainerBuildInStringLess(const char **pcaStringFirst, const char **pcaStringSecond);
static bool _basContainerBuildInCharLess(const char *pcCharFirst, const char *pcCharSecond);
static bool _basContainerBuildInBoolLess(const bool *pbBoolFirst, const bool *pbBoolSecond);
static bool _basContainerBuildInShortLess(const short *pshShortFirst, const short *pshShortSecond);
static bool _basContainerBuildInIntLess(const int *piIntFirst, const int *piIntSecond);
static bool _basContainerBuildInLongLess(const long *plLongFirst, const long *plLongSecond);
static bool _basContainerBuildInLongLongLess(const long long *pllLongLongFirst, const long long *pllLongLongSecond);
static bool _basContainerBuildInFloatLess(const float *pfFloatFirst, const float *pfFloatSecond);
static bool _basContainerBuildInDoubleLess(const double *pdDoubleFirst, const double *pdDoubleSecond);
static bool _basContainerBuildInLongDoubleLess(const long double *pldLongDoubleFirst, const long double *pldLongDoubleSecond);

BAS_EXPORT T_TypeInfo _basContainerCreateTypeInfo(const char *caTypeName, const size_t nTypeSize, ...)
{
    T_TypeInfo tTypeInfo;
    va_list varList;
    
    memset(&tTypeInfo, 0, sizeof(tTypeInfo));

    if (strcmp(caTypeName, "BUILDIN_STRING") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_STRING;
        tTypeInfo.nTypeSize = sizeof(char *);
        tTypeInfo.pfnDestroy = (T_DestroyFunction)_basContainerBuildInStringDestory;
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInStringLess;
        tTypeInfo.pfnCopy = (T_CopyFunction)_basContainerBuildInStringCopy;
    }
    else if (strcmp(caTypeName, "BUILDIN_CHAR") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_CHAR;
        tTypeInfo.nTypeSize = sizeof(char);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInCharLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_BOOL") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_BOOL;
        tTypeInfo.nTypeSize = sizeof(bool);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInBoolLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_SHORT") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_SHORT;
        tTypeInfo.nTypeSize = sizeof(short);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInShortLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_INT") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_INT;
        tTypeInfo.nTypeSize = sizeof(int);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInIntLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_LONG") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_LONG;
        tTypeInfo.nTypeSize = sizeof(long);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInLongLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_LONGLONG") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_LONGLONG;
        tTypeInfo.nTypeSize = sizeof(long long);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInLongLongLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_FLOAT") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_FLOAT;
        tTypeInfo.nTypeSize = sizeof(float);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInFloatLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_DOUBLE") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_DOUBLE;
        tTypeInfo.nTypeSize = sizeof(double);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInDoubleLess;
    }
    else if (strcmp(caTypeName, "BUILDIN_LONGDOUBLE") == 0)
    {
        tTypeInfo.iDataType = BUILDIN_LONGDOUBLE;
        tTypeInfo.nTypeSize = sizeof(long double);
        tTypeInfo.pfnLess = (T_CompareFunction)_basContainerBuildInLongDoubleLess;
    }
    else
    {
        tTypeInfo.iDataType = 0;
        tTypeInfo.nTypeSize = nTypeSize;
        va_start(varList, nTypeSize);
        do
        {
            if (BAS_PARM_END == 
                (tTypeInfo.pfnDestroy = va_arg(varList, T_DestroyFunction)))
            {
                tTypeInfo.pfnDestroy = NULL;
                break;
            }

            if (BAS_PARM_END ==
                (tTypeInfo.pfnCopy = va_arg(varList, T_CopyFunction)))
            {
                tTypeInfo.pfnCopy = NULL;
                break;
            }
            
            if (BAS_PARM_END ==
                (tTypeInfo.pfnLess = va_arg(varList, T_CompareFunction)))
            {
                tTypeInfo.pfnLess = NULL;
                break;
            }

            if (BAS_PARM_END ==
                (tTypeInfo.pfnInit = va_arg(varList, T_InitFunction)))
            {
                tTypeInfo.pfnInit = NULL;
                break;
            }
        } while (false);
        va_end(varList);
    }

    return tTypeInfo;
}

BAS_EXPORT int _basContainerGetVaArgValue(const T_TypeInfo *ptTypeInfo, void *pvOutput, va_list *pVarList)
{
    char *caString = NULL;

    //这里必须考虑可变参数类型提升
    switch (ptTypeInfo->iDataType)
    {
    case BUILDIN_STRING:
        caString = va_arg(*pVarList, char *);
        if (ptTypeInfo->pfnCopy(pvOutput, &caString) != 0)
        {
            CONTAINER_LOG_ERROR("pfnCopy failed");
            return -1;
        }
        break;

    case BUILDIN_CHAR:
        *(char *)pvOutput = (char)va_arg(*pVarList, int);
        break;

    case BUILDIN_BOOL:
        *(bool *)pvOutput = (bool)va_arg(*pVarList, int);
        break;

    case BUILDIN_SHORT:
        *(short *)pvOutput = (short)va_arg(*pVarList, int);
        break;

    case BUILDIN_INT:
        *(int *)pvOutput = va_arg(*pVarList, int);
        break;

    case BUILDIN_LONG:
        *(long *)pvOutput = va_arg(*pVarList, long);
        break;

    case BUILDIN_LONGLONG:
        *(long long *)pvOutput = va_arg(*pVarList, long long);
        break;

    case BUILDIN_FLOAT:
        *(float *)pvOutput = (float)va_arg(*pVarList, double);
        break;

    case BUILDIN_DOUBLE:
        *(double *)pvOutput = va_arg(*pVarList, double);
        break;

    case BUILDIN_LONGDOUBLE:
        *(long double *)pvOutput = va_arg(*pVarList, long double);
        break;

    default:
        //如果指定了拷贝函数, 则调用拷贝函数进行深拷贝
        if (ptTypeInfo->pfnCopy != NULL)
        {
            if (ptTypeInfo->pfnCopy(pvOutput, va_arg(*pVarList, void *)) != 0)
            {
                CONTAINER_LOG_ERROR("pfnCopy failed");
                return -1;
            }
        }
        else
        {
            memcpy(pvOutput, va_arg(*pVarList, void *), ptTypeInfo->nTypeSize);
        }
        break;
    }

    return 0;
}

/**
 * @brief 内置STRING类型销毁函数
 */
static void _basContainerBuildInStringDestory(char **pcaString)
{
    free(*pcaString);
}

/**
 * @brief 内置STRING类型拷贝函数
 */
static int _basContainerBuildInStringCopy(char **pcaStringTo, const char **pcaStringFrom)
{
    if (NULL == (*pcaStringTo = (char *)malloc(strlen(*pcaStringFrom) + 1)))
    {
        CONTAINER_LOG_ERROR("malloc failed");
        return -1;
    }
   
    strcpy(*pcaStringTo, *pcaStringFrom);

    return 0;
}

/**
 * @brief 内置STRING类型小于比较函数
 */
static bool _basContainerBuildInStringLess(const char **pcaStringFirst, const char **pcaStringSecond)
{
    return strcmp(*pcaStringFirst, *pcaStringSecond);
}

/**
 * @brief 内置CHAR类型小于比较函数
 */
static bool _basContainerBuildInCharLess(const char *pcCharFirst, const char *pcCharSecond)
{
    return *pcCharSecond < *pcCharSecond;
}

/**
 * @brief 内置BOOL类型小于比较函数
 */
static bool _basContainerBuildInBoolLess(const bool *pbBoolFirst, const bool *pbBoolSecond)
{
    return *pbBoolFirst < *pbBoolSecond;
}

/**
 * @brief 内置SHORT类型小于比较函数
 */
static bool _basContainerBuildInShortLess(const short *pshShortFirst, const short *pshShortSecond)
{
    return *pshShortFirst < *pshShortSecond;
}

/**
 * @brief 内置INT类型小于比较函数
 */
static bool _basContainerBuildInIntLess(const int *piIntFirst, const int *piIntSecond)
{
    return *piIntFirst < *piIntSecond;
}

/**
 * @brief 内置LONG类型小于比较函数
 */
static bool _basContainerBuildInLongLess(const long *plLongFirst, const long *plLongSecond)
{
    return *plLongFirst < *plLongSecond;
}

/**
 * @brief 内置LONG LONG类型小于比较函数
 */
static bool _basContainerBuildInLongLongLess(const long long *pllLongLongFirst, const long long *pllLongLongSecond)
{
    return *pllLongLongFirst < *pllLongLongSecond;
}

/**
 * @brief 内置FLOAT类型小于比较函数
 */
static bool _basContainerBuildInFloatLess(const float *pfFloatFirst, const float *pfFloatSecond)
{
    return *pfFloatFirst < *pfFloatSecond;
}

/**
 * @brief 内置DOUBLE类型小于比较函数
 */
static bool _basContainerBuildInDoubleLess(const double *pdDoubleFirst, const double *pdDoubleSecond)
{
    return *pdDoubleFirst < *pdDoubleSecond;
}

/**
 * @brief 内置LONG DOUBLE类型小于比较函数
 */
static bool _basContainerBuildInLongDoubleLess(const long double *pldLongDoubleFirst, const long double *pldLongDoubleSecond)
{
    return *pldLongDoubleFirst < *pldLongDoubleSecond;
}

/** @} */
